namespace How_toEval.tests;

public class Tests
{
    private dynamic Eval(string exp)
    {
        var lexer = new Lexer(exp);
        var tokens = lexer.Process();
        var parser = new Parser(tokens);
        var abstractSyntaxTree = parser.Unit();
        Evaler evaler = new Evaler(new VarScanner(new DefineScanner()), abstractSyntaxTree);
        return evaler.Run();
    }

    private string TreeString(string exp)
    {
        var lexer = new Lexer(exp);
        var tokens = lexer.Process();
        var parser = new Parser(tokens);
        var abstractSyntaxTree = parser.Unit();
        return abstractSyntaxTree.ToString();
    }

    [SetUp]
    public void Setup()
    {
    }

    [Test]
    public void Test1()
    {
        Assert.That(TreeString("1+1") == "(+ 1 1)");
    }

    [Test]
    public void Test2()
    {
        Assert.AreEqual(Eval("1+1"), 2, 0.000001);
    }

    [Test]
    public void Test3()
    {
        var test = @"
                {
                    x : int := 99,
                    y := x,
                    g := fun(i: int, j: int) => float
                        i + j - z,
                    f := fun(x, y) {
                        z := 0,
                        x - y + g(1, 1)
                    },
                    z := 2,
                    f(1, 1),
                    0.6 * 0.3134 + (0.4):float * ((0.61 - 0.67) / 0.67),
                    (fun(a, b) => int f(a, b))(1, 2)
                }
            ";
        Assert.AreEqual(Eval(test), -1, 0.000001);
        Assert.That(TreeString(test) == "(begin (define x 99) (define y x) (define g (lambda : float (i : int j : int) (- (+ i j) z))) (define f (lambda (x y) (begin (define z 0) (+ (- x y) (g 1 1))))) (define z 2) (f 1 1) (+ (* 0.6 0.3134) (* 0.4 : float (/ (- 0.61 0.67) 0.67))) ((lambda : int (a b) (f a b)) 1 2))");
    }

    [Test]
    public void Test4()
    {
        var test = @"{
            ifelse := fun(condition, λ1, λ2) {
                condition(λ1, λ2)
            },

            true := fun(λ1, λ2) {
                λ1()
            },

            false := fun(λ1, λ2) {
                λ2()
            },

            ifelse(true, fun() 1, fun() 2),

            print(input())
        }";
        var sr = new StringReader("Hello");
        Console.SetIn(sr);
        var sw = new StringWriter();
        Console.SetOut(sw);
        Assert.AreEqual(Eval(test), 1, 0.000001);
        Assert.AreEqual("Hello", sw.ToString());
    }
}